package org.infodavid.common.impl.services.security;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import javax.persistence.PersistenceException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.infodavid.common.annotations.AImplementation;
import org.infodavid.common.annotations.AImplementation.ERuntime;
import org.infodavid.common.dto.IUserGroupListItem.EUserRole;
import org.infodavid.common.impl.services.ServiceUtil;
import org.infodavid.common.impl.services.helpers.ServiceHelper;
import org.infodavid.common.model.IUser;
import org.infodavid.common.model.IUserGroup;
import org.infodavid.common.model.criteria.IAccessControlListCriteria;
import org.infodavid.common.model.security.IAccessControlList;
import org.infodavid.common.persistence.AccessControlDataService;
import org.infodavid.common.services.IApplicationContext;
import org.infodavid.common.services.UserGroupService;
import org.infodavid.common.services.exceptions.ServiceException;
import org.infodavid.common.services.security.AccessControlService;

/**
 * The Class AccessControlServiceImpl.
 */
@AImplementation(value = AccessControlService.class, runtime = ERuntime.DEFAULT)
public class AccessControlServiceImpl extends AccessControlService {

  /** The Constant log. */
  private static final Log LOGGER = LogFactory.getLog(AccessControlServiceImpl.class);

  /** The helper. */
  private final ServiceHelper<IAccessControlList,IAccessControlList,Long,IAccessControlListCriteria> helper;

  /**
   * The Constructor.
   */
  protected AccessControlServiceImpl() {
    super();

    helper =
        new ServiceHelper<IAccessControlList,IAccessControlList,Long,IAccessControlListCriteria>(
            AccessControlDataService.getInstance());
  }

  /*
   * See super class or interface. (non-Javadoc)
   * @see org.infodavid.common.services.AbstractEntityService#add(org.infodavid.common.services.
   * IApplicationContext, org.infodavid.common.model.IDataObject)
   */
  @Override
  public long add(final IApplicationContext context, final IAccessControlList value)
      throws PersistenceException, ServiceException {
    helper.validate(context, getValidator(), value);

    final IInterceptorEvent evt = preExecute(context, ADD_ACTION, value);
    final long key = helper.add(context, value).longValue();

    postExecute(evt.setResults(value));

    LOGGER.info("A new acl has been added (" + key + ')');

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("A new acl has been added: " + value + " (" + key + ')');
    }

    return key;
  }

  @Override
  public IAccessControlList findByName(final IApplicationContext context, final String value)
      throws PersistenceException, ServiceException {
    if (StringUtils.isEmpty(value)) {
      throw new IllegalArgumentException(IUserGroup.NAME);
    }

    ServiceUtil.validate(context);

    final IInterceptorEvent evt = preExecute(context, FIND_ACTION, value);
    final IAccessControlList result =
        ((AccessControlDataService)helper.getDataService()).findByName(
            context.getPersistenceSession(), value);

    postExecute(evt.setResults(result));

    return result;
  }

  /*
   * See super class or interface. (non-Javadoc)
   * @see org.infodavid.common.services.AbstractEntityService#get(org.infodavid.common.services.
   * IApplicationContext, long)
   */
  @SuppressWarnings("boxing")
  @Override
  public IAccessControlList get(final IApplicationContext context, final long key)
      throws PersistenceException, ServiceException {
    helper.validate(context, key);

    final IInterceptorEvent evt = preExecute(context, GET_ACTION, key);
    final IAccessControlList result = helper.get(context, key);

    postExecute(evt.setResults(result));

    return result;
  }

  /*
   * See super class or interface. (non-Javadoc)
   * @see org.infodavid.common.services.AbstractEntityService#getEntities(org.infodavid.common.services.
   * IApplicationContext, java.util.List, org.infodavid.common.services.ISearchCriteria)
   */
  @Override
  public long findEntities(final IApplicationContext context,
      final List<IAccessControlList> entities, final IAccessControlListCriteria criteria)
      throws PersistenceException, ServiceException {
    ServiceUtil.validate(context);
    helper.validate(entities);

    final IInterceptorEvent evt = preExecute(context, FIND_ACTION, criteria);
    final long result =
        ((AccessControlDataService)helper.getDataService()).find(context.getPersistenceSession(),
            entities, criteria);

    postExecute(evt.setResults(entities));

    return result;
  }

  /*
   * See super class or interface. (non-Javadoc)
   * @see org.infodavid.common.services.AbstractEntityService#remove(org.infodavid.common.services.
   * IApplicationContext, long)
   */
  @SuppressWarnings("boxing")
  @Override
  public void remove(final IApplicationContext context, final long key)
      throws PersistenceException, ServiceException {
    helper.validate(context, key);

    final IInterceptorEvent evt = preExecute(context, REMOVE_ACTION, key);
    final IAccessControlList existing = helper.get(context, Long.valueOf(key));

    helper.remove(context, existing);
    postExecute(evt.setResults(existing));

    LOGGER.info("An acl has been removed (" + key + ')');

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("An acl has been removed: " + existing + " (" + key + ')');
    }
  }

  /*
   * See super class or interface. (non-Javadoc)
   * @see org.infodavid.common.services.AbstractEntityService#update(org.infodavid.common.services.
   * IApplicationContext, org.infodavid.common.model.IDataObject)
   */
  @Override
  public void update(final IApplicationContext context, final IAccessControlList value)
      throws PersistenceException, ServiceException {
    helper.validate(context, getValidator(), value);

    final IInterceptorEvent evt = preExecute(context, UPDATE_ACTION, value);

    helper.update(context, value);
    postExecute(evt.setResults(value));

    LOGGER.info("An acl has been updated (" + value.getKey() + ')');

    if (LOGGER.isDebugEnabled()) {
      LOGGER.debug("An acl has been updated: " + value + " (" + value.getKey() + ')');
    }
  }

  /*
   * See super class or interface. (non-Javadoc)
   * @see
   * org.infodavid.common.services.security.ISecurityPolicy#isAllowed(org.infodavid.common.services.
   * IApplicationContext, java.util.Collection, java.lang.String)
   */
  public boolean isAllowed(final IApplicationContext context, final Collection<IUserGroup> groups,
      final String feature) {
    final boolean isDebugEnabled = LOGGER.isDebugEnabled();
    boolean allowed = false;

    if (groups == null || groups.isEmpty()) {
      if (isDebugEnabled) {
        LOGGER.debug("No group specified");
      }
    }
    else {
      final Iterator<IUserGroup> ite = groups.iterator();
      IUserGroup group;

      while (!allowed && ite.hasNext()) {
        group = ite.next();
        allowed = isAllowed(context, group, feature);
      }
    }

    if (isDebugEnabled) {
      LOGGER.debug("Access is " + (allowed ? "allowed" : "denied"));
    }

    return allowed;
  }

  /*
   * See super class or interface. (non-Javadoc)
   * @see
   * org.infodavid.common.services.security.ISecurityPolicy#isAllowed(org.infodavid.common.services.
   * IApplicationContext, org.infodavid.common.model.IUser, java.lang.String)
   */
  public boolean isAllowed(final IApplicationContext context, final IUser user, final String feature) {
    final boolean isDebugEnabled = LOGGER.isDebugEnabled();
    Boolean allowed = null;

    if (user == null) {
      if (isDebugEnabled) {
        LOGGER.debug("No user specified");
      }

      allowed = Boolean.FALSE;
    }
    else {
      if (isDebugEnabled) {
        LOGGER.debug("Validating security for user: " + user.getName() + " and action: " + feature);
      }

      if (IUser.ADMINISTRATOR.equals(user.getName())) {
        allowed = Boolean.TRUE;
      }
      else if (IUser.SYSTEM.equals(user.getName())) {
        allowed = Boolean.TRUE;
      }
      else {
        // TODO
        allowed = Boolean.TRUE;
      }
    }

    if (isDebugEnabled) {
      LOGGER.debug("Access is " + (allowed.booleanValue() ? "allowed" : "denied"));
    }

    return allowed.booleanValue();
  }

  /*
   * See super class or interface. (non-Javadoc)
   * @see
   * org.infodavid.common.services.security.ISecurityPolicy#isAllowed(org.infodavid.common.services.
   * IApplicationContext, org.infodavid.common.model.IGroup, java.lang.String)
   */
  public boolean isAllowed(final IApplicationContext context, final IUserGroup group,
      final String feature) {
    final boolean isDebugEnabled = LOGGER.isDebugEnabled();
    boolean allowed = false;

    if (group == null) {
      if (isDebugEnabled) {
        LOGGER.debug("No group specified");
      }
    }
    else {
      if (isDebugEnabled) {
        LOGGER.debug("Validating security for group: " + group.getName() + " and action: "
            + feature);
      }

      if (UserGroupService.ADMINISTRATORS.equals(group.getName())
          || group.getRole() == EUserRole.ADMINISTRATORS) {
        allowed = true;
      }
      else if (UserGroupService.SYSTEM.equals(group.getName()) || group.getRole() == EUserRole.SYSTEM) {
        allowed = true;
      }
      else {
        // TODO
        allowed = true;
      }
    }

    if (isDebugEnabled) {
      LOGGER.debug("Access is " + (allowed ? "allowed" : "denied"));
    }

    return allowed;
  }
}
